// 01命令行改代码
// 在命令行中安装TypeScript全局依赖，以便在任何目录下都能使用tsc命令
// npm i typescript -g
// 使用tsc命令将TypeScript文件编译为JavaScript文件
// tsc index.ts



// 02自动化编译
// 安装ts-node模块
// tsc --init
// tdc --watch



// 03数据类型
let num:number = 10
let str:string = "hello"
let bool:boolean = true
let zi: "hello"
// 字面量类型，只能存放hello



// 04总览所有类型
// js：
// string  number  boolean  null underfined  bigint  symbol  object(Array  Function  Data Error)
// ts:
// 以上 + 
// any unknown never void tuple enum 自定义：type interface



// 05 any类型
// 特点
// 可以赋值给任意类型
// 可以赋值给任意变量
let a:any 
a = 10
console.log(a)
// 显示
let b
// 隐式

a = 10
console.log(typeof a)
a = "hello"
console.log(typeof a)

// any可以给任意类型赋值，但同时会影响到其他类型
let c:number
c = a
console.log(typeof c)



// 06 unknown类型
// 特点
// 也可以赋值给任意类型
// 但会报错
let d:unknown
let e:number
d = 10
// e = d
// console.log(e.toUpperCase())

// 解决报错：
// 1.加条件判断
if(typeof d === "number"){
    e = d
}
// 2.类型断言
e = d as number
e = <number>d



//07never类型
// 特点
// 1.never类型不能有任何值
// 不要给变量赋值为never类型
let f:never
// f = 10 // 报错

// 2.never可限制函数的返回值（函数没有返回值）
function fn():never{
    throw new Error("错误")
}

// 3.有时是typescript推断出的
let g:string = "hello"
if(typeof g === "string"){
    console.log(g.toUpperCase())
}
else{
    console.log(g)// g为never
}



// 08 void类型
// 特点
// 1.void类型一般用于函数没有返回值的情况（限制）
// 2.void类型可接受undefined(函数可隐式返回值)
function fn1(msg:string):void{
    console.log(msg)
}// js函数默认返回值为undefined

// 3.不能对void函数的返回值进行操作
let h = fn1("hello")
// h.toUpperCase()// 报错



// 09object类型
// （1）object类型
// 特点
// 1.可以存所有非原始类型【数组，函数，对象等.即除了number string boolean以及null undefined】，范围宽泛，一般不使用
let i:object
i = {
    name:"张三",
}
i = [1,2,3]
i = function(){}
i = new Date()
i = {}
// （2）Object类型
// 特点
// 1.除了null和undefined，其他类型都可以赋值给Object类型，范围更大，基本不用

// （3）声明对象类型格式
// 1.对象字面量
let j:{name:string,age?:number}// 声明对象的属性和属性值的类型
j = {// 赋值
    name:"张三",//也可以用封号和回车分隔
    age:18//必须两个都写，若想少写，声明加问号
}
// 2.索引签名
let k:{name:string,
    age:number,
    [key:string]:any}//可任意添加值
k = {
    name:"张三",
    age:18,
    sex:"男"
}

// （4）声明函数格式
let count:(a:number,b:number)=>number// 声明函数的形参和返回值的类型(箭头函数)
count = function(x,y){// 赋实参
    return x+y
}

// (5)声明数组类型
let arr1:number[]// 声明数组的元素类型
arr1 = [1,2,3]
let arr2:Array<number>// 声明数组的元素类型(泛型)
arr2 = [1,2,3]



// 10tuple元组类型
// 前言
// 元组类型不是一个关键字，是数组类型的扩展，数组类型可以存放任意类型的元素，元组类型可以存放指定类型的元素
// 特点
// 1.元组类型可以存放指定类型的元素
let tup1:[number,string,boolean]// 声明元组的元素类型
let tup2:[number,string,boolean?]// 可加问号,表示可省略
let tup3:[number,...string[],boolean]// 表示可加任意数量的字符串,但三种数据类型位置需要固定

tup1 = [1,"hello",true]
tup2 = [1,"hello",true]
tup3 = [1,"hello","world","!","!",true]



// 11enum枚举类型
// 前言
// 1.1枚举类型是一种特殊的类型，它可以将一组相关的值组合在一起，方便使用（有了含义命名后，不容易出错）
// 1.2常亮
// 类型
// 2.1数字枚举（自动递增，有数字与值的反向映射
enum Direction{
    Up,
    Down,
    Left,
    Right
}
function walk(data:Direction){//有效避免输入down时拼写错误导致的无法对应
    switch(data){
        case Direction.Up:
            console.log("上")
        case Direction.Down:
            console.log("下")
        case Direction.Left:
            console.log("左")
        case Direction.Right:
            console.log("右")
    }
}
// 2.2字符串枚举（无反向映射，常用在API）
enum Color{
    red="hong",
    blue="lan",
    yellow="huang"
}
function choose(point:Color){
    switch(point){
        case Color.blue:
            console.log("蓝")
        case Color.red:
            console.log("红")
        case Color.yellow:
            console.log("黄")
    }
}
// 2.3常量枚举（把枚举成员“联合”，使得转出的js代码量少）
const enum Direction2{
    up,
    down,
    left,
    right
}
console.log(Direction2.up)



// 12type
// 前言
// type可定义一种新类型
// 用法
// 1.1起别名
type num = number;
let price:num = 10
// 1.2联合（一种高级类型，表示某值可以是几种不同类型之一）
type Status = number | string//两种数据类型种选
type Gender = "男" | "女"//两种字面类型中选

function logGender(xxx:Gender){
    console.log(xxx)
}
logGender("男")//只能在男女中选
// 1.3交叉类型（多类型取交集，即同时满足多个type类型）
type Area = {//类似结构struct，定义了新类型Area
    height:number;
    width:number
};
type Room = {
    lou:number;
    cell:number;
    room:string
};

type House = Area & Room;//两者的键值对都需满足

const house:House = {//House类型的变量是house
    height:180,
    width:75,
    lou:6,
    cell:4,
    room:"701"
};



//13void的特殊情况
// 1.1边定义边声明，函数返回值只能为undefined
function auv(isBeijing:boolean){
    console.log(isBeijing)
}
// 1.2用type先声明后定义，函数返回值任意
type LogFunc = () => void//先声明新函数类型：()=>void    //()=>void为返回值为void类型的无形参函数

const f1:LogFunc = function(){//再定义类型下的f1
    return 66//返回值类型不为void，但未报错
}



// 14class类
// 1.类
class Person{
    name:string//数据成员
    age:number
    constructor(name:string,age:number){//构造函数，形参为name和age
        this.name = name//将形参name传给数据成员name
        this.age = age
    }
    speak(){//成员函数
        console.log(`我叫：${this.name}，今年${this.age}岁了`)
    }
}//结尾不加;
const p1 = new Person("李同学",18)//构建对象
p1.speak()


// 2.继承
class Student extends Person{//继承
    grade:string//新数据成员
    constructor(name:string,age:number,grade:string){//子类的构造函数
        super(name,age)//继承父类的数据成员
        this.grade = grade//初始化新数据成员
    }
    study(){//新成员函数
        console.log(`${this.name}正在努力学习中……`)
    }
    override speak(): void {//同名覆盖，写override确保父子类函数同名
        console.log(this.grade)
    }
}

const s1 = new Student("刘同学",19,"高三")//构建对象
s1.study()//调用study
s1.speak()



// 15属性修饰符public protected private readonly
// （1）public
// 1.1默认为public,范围与c++一样
// 1.2可简写，如class Person{constructor(public name:string,public age:number){}}

// （2）protected private与c++基本一致

// （3）readonly
class Car{
    public readonly vin:number;
    constructor(vin:number){
        this.vin = vin
    }
}
const c1 = new Car(123456)
console.log(c1.vin)//可读
// c1.vin = 789    //不可改


// 16抽象类
// 特点
// 1.无法被实例化，存在意义为被继承，其中可有普通方法和抽象方法

abstract class Package{
    constructor(public weight:number) {}
    //抽象方法，无函数体，等着子类来扩充
    abstract calculate():number
    //具体方法
    printPackage(){
        console.log(`重量为${this.weight}，运费为${this.calculate}`)
    }
}

class StandardPackage extends Package{//抽象类的子类
    constructor(weight:number,public perPrice:number){super(weight)}
    calculate(): number {//增写calculate的函数体，因为此时已清楚Package的种类及计算方法
        return this.weight * this.perPrice;
    }
}

const stand1 = new StandardPackage(55,12)//子类的实例
stand1.printPackage()//调用抽象类的具体方法来显示价格



// 17interface接口
// 特点
// 可规范类、对象、函数

// 1.接口与类
interface IPerson {
    weight:number
    name:string
    speak(n:number):void
}

class Person1 implements IPerson{
    constructor(
        public weight:number,
        public name:string
    ){}
    speak(n: number) : void{
        for(let i = 0;i<n;i++){
            console.log(`我是${this.name}，体重为${this.weight}kg`)
        }
    }
}

const p2 = new Person1(77,"lxh")
p2.speak(4)

// 2.接口与对象（可将接口视作类型）
interface UserInterface {
    name:string
    readonly gerder:string
    age?:number
    run:(n:number)=>void
}

const Qwe:UserInterface = {
    name:"lxh",
    gerder:"man",
    age:18,
    run(n){
        console.log(`跑了${n}米`)
    }
};
Qwe.run(50)

// 3.接口与函数
interface ICount {
    (a:number,b:number):number;
}

const count1:ICount = (x,y) => {
    return x+y
}

// 4.接口的继承
interface IStaff {
    name:string
    age:number
}

interface ITeacher extends IStaff {//直接写多加的属性即可
    grade:string
}

const teacher1:ITeacher = {
    name:"lxh",
    age:18,
    grade:"fifteen"
}

// 5.接口的自动合并
interface IPerson2 {
    name:string
    age:number
}
interface IPerson2 {
    grade:string
}
const human:IPerson2 = {//IPerson2的属性加和了
    name:"lxh",
    age:18,
    grade:"dayi"
}



// 18概念辨析
// 1.interface和type的区别
// 1.1偏向：前者偏向类和对象，后者偏向类型别名、交叉类型（&）、联合类型（|）
// 1.2两者多数情况下可转换（前者的合并和继承 = 后者的交叉）

// 2.interface和抽象类abstract
// 2.1都可以定义类的格式
// 2.2前者只能描述结构，不能实现代码；后者可含抽象与具体的方法 
// 2.3一个类可实现多个接口，但只能有一个抽象类



//19泛型（把数据类型视作变量）
// 1.泛型函数
function LogData<T,U>(data1:T,data2:U):T|U {//数据类型是变量T和U,返回值为T或U类型
    console.log(data1)
    console.log(data2)
    return Date.now() %2 ? data1 : data2
} 

LogData<number,boolean>(100,true)//现在T是number
LogData<string,number>("123",123)//现在T是string

// 2.多个泛型

// 3.泛型接口
interface IPerson3<T> {//T可为任意类型（包括type自定义出来的）
    name:string
    extroInfo:T
}

let p:IPerson3<number> = {
    name:"lxh",
    extroInfo:123
}



// 20类型声明文件
// 常用.d.ts后缀文件，为现有js文件提供类型信息，便于ts使用js的库和模块